﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices;
using HTTT.DTO;
using System.DirectoryServices.AccountManagement;
using System.Configuration;
using HTTT.Utilities;
using HTTT.DataAccess;

namespace HTTT.AD
{
    public class OU
    {
        #region Private

        private readonly string _connectionUsername;
        private readonly string _connectionPassword;

        /// <summary>
        /// Gets a principal context.
        /// </summary>
        /// <returns></returns>
        private PrincipalContext GetPrincipalContext(string domain)
        {
            return new PrincipalContext(ContextType.Domain,
                    domain,
                    null,
                    ContextOptions.Negotiate,
                    _connectionUsername,
                    _connectionPassword);
        }

        /// <summary>
        /// Gets the principal context.
        /// </summary>
        /// <param name="domain">The domain.</param>
        /// <param name="dn">The location to bind to (OU).</param>
        /// <returns></returns>
        private PrincipalContext GetPrincipalContext(string domain, string dn)
        {
            return new PrincipalContext(ContextType.Domain,
                    domain,
                    dn,
                    ContextOptions.Negotiate,
                    _connectionUsername,
                    _connectionPassword);
        }

        /// <summary>
        /// Gets the name of the domain from the username.
        /// </summary>
        /// <param name="username">The username.</param>
        /// <returns></returns>
        private static string GetDomainNameFromUsername(string username)
        {
            if (username.Contains("@"))
            {
                var i = username.IndexOf("@") + 1;
                if (i > 0)
                    return username.Remove(0, i);
            }
            else
            {
                var i = username.IndexOf("\\");
                if (i >= 0)
                    return username.Remove(i);
            }
            return ConfigurationManager.ConnectionStrings["ADService"].ConnectionString.Substring(7).ToLower();
        }

        private GroupPrincipal GetGroup(string sGroupName, string domain)
        {
            PrincipalContext oPrincipalContext = GetPrincipalContext(domain);

            GroupPrincipal oGroupPrincipal = GroupPrincipal.FindByIdentity(oPrincipalContext, sGroupName);
            return oGroupPrincipal;
        }

        /// <summary>
        /// Sets the property.
        /// </summary>
        /// <param name="d">The directory entry to use.</param>
        /// <param name="p">The parameter to assign.</param>
        /// <param name="v">The value to use.</param>
        private static void SetProperty(DirectoryEntry d, string p, string v)
        {
            d.Properties[p].Value = String.IsNullOrWhiteSpace(v) ? null : v;
        }

        #endregion

        #region Constructor

        ///<summary>
        /// Constructor for the Groups Class
        ///</summary>
        ///<param name="user">AD Username</param>
        ///<param name="pass">AD Password</param>
        ///<returns>Nothing</returns>
        ///<remarks></remarks>
        public OU(string user, string pass)
        {
            _connectionUsername = user;
            _connectionPassword = pass;

        }


        #endregion

        private DirectoryEntry GetDirectoryEntry(string path)
        {
            return new DirectoryEntry(path, _connectionUsername, _connectionPassword, AuthenticationTypes.Secure);
        }

        public ReturnValue<bool> CreateNewOU(string ouName, string domain, string path)
        {

            if (String.IsNullOrWhiteSpace(domain)) return new Utilities.ReturnValue<bool>(false, "Domain không đúng");
            if (!String.IsNullOrWhiteSpace(_connectionUsername) && !String.IsNullOrWhiteSpace(_connectionPassword))
            {
                try
                {
                    string ouPath = "LDAP://" + domain + "/" + path;
                    string newOUName = "OU=" + ouName;
                    using (DirectoryEntry DeBase = GetDirectoryEntry(ouPath))
                    {

                        DirectorySearcher ouSrc = new DirectorySearcher(DeBase);
                        ouSrc.Filter = "(" + newOUName + ")";
                        ouSrc.SearchScope = SearchScope.Subtree;
                        SearchResult srOU = ouSrc.FindOne();
                        if (srOU == null)
                        {
                            // Add new OU
                            DirectoryEntry Entry = DeBase.Children.Add(newOUName, "organizationalUnit");
                            Entry.CommitChanges();
                            return new Utilities.ReturnValue<bool>(true, "AD - Thêm mới OU thành công");
                        }
                        else
                        {
                            return new Utilities.ReturnValue<bool>(false, "AD - OU đã tồn tại");
                        }
                    }
                }
                catch (Exception ex)
                {
                    return new Utilities.ReturnValue<bool>(false, "Có lỗi xảy ra " + ex.Message);
                }
            }
            return new ReturnValue<bool>(false, "Tài khoản xác thực với AD không đúng");

        }

        /// <summary>
        /// Get all OU
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public List<OUDTO> GetOUListByPath(DirectoryEntry dirEntry)
        {
            try
            {
                List<OUDTO> ous = new List<OUDTO>();
                DirectorySearcher directorySearch = new DirectorySearcher(dirEntry);
                directorySearch.Filter = "(&(objectClass=organizationalUnit))";
                directorySearch.SearchScope = SearchScope.Subtree;
                directorySearch.PropertiesToLoad.Add(ADOUProperties.DISTINGUISHEDNAME);
                directorySearch.PropertiesToLoad.Add(ADOUProperties.NAME);

                var results = directorySearch.FindAll();

                foreach (SearchResult entry in results)
                {
                    string Name = entry.GetDirectoryEntry().Properties["name"].Value.ToString();
                    string Path = entry.GetDirectoryEntry().Properties["distinguishedName"].Value.ToString();
                    //Path = Util.Reverse(Path);
                    ous.Add(new OUDTO(Name, entry.Path, Path));
                }

                results.Dispose();
                directorySearch.Dispose();
                return ous;
            }
            catch (Exception)
            {
                return null;
            }
        }

        internal ReturnValue<bool> UpdateOU(DataAccess.HT_OU newEditOU, DataAccess.HT_OU oldEditOU, string domain)
        {
            if (String.IsNullOrWhiteSpace(domain)) return new Utilities.ReturnValue<bool>(false, "Domain không đúng");
            if (!String.IsNullOrWhiteSpace(_connectionUsername) && !String.IsNullOrWhiteSpace(_connectionPassword))
            {
                try
                {
                    // Get parent of new OU path
                    string NewParentOUPath = newEditOU.DistinguishedName;
                    NewParentOUPath = NewParentOUPath.Substring(NewParentOUPath.IndexOf(',') + 1);

                    // Get parent of Old OU path
                    string OldParentOUPath = oldEditOU.DistinguishedName;
                    OldParentOUPath = OldParentOUPath.Substring(OldParentOUPath.IndexOf(',') + 1);

                    if (IsOUExist(newEditOU.Name, NewParentOUPath, domain)) return new ReturnValue<bool>(false, "OU đã tồn tại");

                    if (NewParentOUPath != OldParentOUPath)
                    {
                        // Move and rename
                        string NewParentFullPath = "LDAP://" + domain + "/" + NewParentOUPath;
                        DirectoryEntry newDir = GetDirectoryEntry(NewParentFullPath);

                        string CurrentFullPath = "LDAP://" + domain + "/" + oldEditOU.DistinguishedName;
                        DirectoryEntry CurrentEntry = GetDirectoryEntry(CurrentFullPath);
                        CurrentEntry.MoveTo(newDir, "OU=" + newEditOU.Name);
                        return new Utilities.ReturnValue<bool>(true, "AD - Cập nhật OU thành công");
                    }
                    else
                    {
                        // Rename OU
                        string CurrentFullPath = "LDAP://" + domain + "/" + oldEditOU.DistinguishedName;
                        DirectoryEntry CurrentEntry = GetDirectoryEntry(CurrentFullPath);
                        CurrentEntry.Rename("OU=" + newEditOU.Name);
                        return new Utilities.ReturnValue<bool>(true, "AD - Cập nhật OU thành công");
                    }
                }
                catch (Exception ex)
                {
                    return new Utilities.ReturnValue<bool>(false, "Có lỗi xảy ra " + ex.Message);
                }
            }
            return new ReturnValue<bool>(false, "Tài khoản xác thực với AD không đúng");
        }


        internal ReturnValue<bool> RenameOU(string newName, DataAccess.HT_OU oldEditOU, string domain)
        {
            if (String.IsNullOrWhiteSpace(domain)) return new Utilities.ReturnValue<bool>(false, "Domain không đúng");
            if (!String.IsNullOrWhiteSpace(_connectionUsername) && !String.IsNullOrWhiteSpace(_connectionPassword))
            {
                try
                {
                    // Get parent of Old OU path
                    string OldParentOUPath = oldEditOU.DistinguishedName;
                    OldParentOUPath = OldParentOUPath.Substring(OldParentOUPath.IndexOf(',') + 1);

                    if (IsOUExist(newName, OldParentOUPath, domain)) return new ReturnValue<bool>(false, "OU đã tồn tại");

                    // Rename OU
                    string CurrentFullPath = "LDAP://" + domain + "/" + oldEditOU.DistinguishedName;
                    DirectoryEntry CurrentEntry = GetDirectoryEntry(CurrentFullPath);
                    CurrentEntry.Rename("OU=" + newName);
                    return new Utilities.ReturnValue<bool>(true, "AD - Đổi tên OU thành công");
                }
                catch (Exception ex)
                {
                    return new Utilities.ReturnValue<bool>(false, "Có lỗi xảy ra " + ex.Message);
                }
            }
            return new ReturnValue<bool>(false, "Tài khoản xác thực với AD không đúng");
        }

        private bool IsOUExist(string ouName, string parentOUPath, string domain)
        {
            string ouPath = "LDAP://" + domain + "/" + parentOUPath;
            DirectoryEntry DeBase = GetDirectoryEntry(ouPath);
            DirectorySearcher ouSrc = new DirectorySearcher(DeBase);
            ouSrc.Filter = "(OU=" + ouName + ")";
            ouSrc.SearchScope = SearchScope.Subtree;
            SearchResult srOU = ouSrc.FindOne();
            return (srOU == null) ? false : true;
        }

        internal ReturnValue<bool> MoveOU(string newParentPath, DataAccess.HT_OU OldOU, string domain)
        {
            if (String.IsNullOrWhiteSpace(domain)) return new Utilities.ReturnValue<bool>(false, "Domain không đúng");
            if (!String.IsNullOrWhiteSpace(_connectionUsername) && !String.IsNullOrWhiteSpace(_connectionPassword))
            {
                try
                {
                    //// Get parent of new OU path
                    //string NewParentOUPath = newEditOU.DistinguishedName;
                    //NewParentOUPath = NewParentOUPath.Substring(NewParentOUPath.IndexOf(',') + 1);

                    //// Get parent of Old OU path
                    //string OldParentOUPath = oldEditOU.DistinguishedName;
                    //OldParentOUPath = OldParentOUPath.Substring(OldParentOUPath.IndexOf(',') + 1);

                    if (IsOUExist(OldOU.Name, newParentPath, domain)) return new ReturnValue<bool>(false, "OU đã tồn tại");

                    // Move
                    string NewParentFullPath = "LDAP://" + domain + "/" + newParentPath;
                    DirectoryEntry newDir = GetDirectoryEntry(NewParentFullPath);

                    string CurrentFullPath = "LDAP://" + domain + "/" + OldOU.DistinguishedName;
                    DirectoryEntry CurrentEntry = GetDirectoryEntry(CurrentFullPath);
                    CurrentEntry.MoveTo(newDir);
                    return new Utilities.ReturnValue<bool>(true, "AD - Di chuyển OU thành công");
                }
                catch (Exception ex)
                {
                    return new Utilities.ReturnValue<bool>(false, "Có lỗi xảy ra " + ex.Message);
                }
            }
            return new ReturnValue<bool>(false, "Tài khoản xác thực với AD không đúng");
        }

        public ReturnValue<bool> DeleteOU(string domain, string path)
        {

            if (String.IsNullOrWhiteSpace(domain)) return new Utilities.ReturnValue<bool>(false, "Domain không đúng");
            if (!String.IsNullOrWhiteSpace(_connectionUsername) && !String.IsNullOrWhiteSpace(_connectionPassword))
            {
                try
                {

                    string ouPath = "LDAP://" + domain + "/" + path;
                    DirectoryEntry Entry = GetDirectoryEntry(ouPath);
                    if(Entry.Children.CountIEnumberable() < 1)
                    {
                        DirectoryEntry Parent = Entry.Parent;
                        Parent.Children.Remove(Entry);
                        Parent.CommitChanges();
                        return new Utilities.ReturnValue<bool>(true, "Xóa OU thành công");
                    }
                    return new Utilities.ReturnValue<bool>(false, "Không thể xóa: OU không trống");
                }
                catch (Exception ex)
                {
                    return new Utilities.ReturnValue<bool>(false, "Có lỗi xảy ra " + ex.Message);
                }
            }
            return new ReturnValue<bool>(false, "Tài khoản xác thực với AD không đúng");

        }
    }

}
